

#include "xmodule_config.h"

#if MODULE_CHECK_ENABLED( LCD )

#include <string.h>

#include "lcd_internal.h"
#include "xDLPS.h"

#if MODULE_CHECK_LOG_ENABLED( LCD )
#include "log.h"
#define LOG     LOG_INFO
#define LOG0    LOG_INFO0 
#define LOG1    LOG_INFO1
#define LOG2    LOG_INFO2
#define LOG3    LOG_INFO3
#else 
#define LOG(...) 
#define LOG0(...)
#define LOG1(...)
#define LOG2(...)
#define LOG3(...)
#endif 

static uint8_t lcd_dlps_index;

#include LCD_DRIVER_FILE

#define LCD_DMA_CHANNEL_NUM                 2 
#define LCD_DMA_CHANNEL_INDEX               GDMA_Channel2
#define LCD_DMA_CHANNEL_IRQ                 GDMA0_Channel2_IRQn 
#define LCD_DMA_CHANNEL_IRQ_HANDLER         GDMA0_Channel2_Handler

volatile lcd_write_ctl_t lcd_wrctl; 

static const T_IO_MODULE_CBS lcd_cbs = 
{
    .init = NULL,
    .enter_dlps = em_lcd_enter_dlps,
    .exit_dlps = em_lcd_exit_dlps,
    .check_allow_dlps = NULL,
    .system_wakeup_check_allow_dlps = NULL, 
};

GUI_RAM_CODE
void lcd_write_data(uint8_t*data, uint16_t len)
{
    LCD_CS_HIGH;
    
    SPI_SendBuffer( LCD_SPI_BUS, data, len ); 
    while( SPI_GetFlagState( LCD_SPI_BUS, SPI_FLAG_TFE | SPI_FLAG_TXE) == false ); 
    platform_delay_us( 10 );
    
    LCD_CS_LOW;
}

GUI_RAM_CODE
void em_lcd_wait_transfer_idle(void)
{
    while( SPI_GetFlagState( LCD_SPI_BUS, SPI_FLAG_TFE | SPI_FLAG_TXE) == false ); 
    platform_delay_us( 40 );    
    LCD_CS_LOW;
}

GUI_RAM_CODE
void LCD_DMA_CHANNEL_IRQ_HANDLER(void)
{
    if( GDMA_GetTransferINTStatus( LCD_DMA_CHANNEL_NUM ) == SET )
    {
        GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer ); 
        GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE );
        
        if( lcd_write_completed() )
        {
            
        }
        GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE );
        
    }
}

static void lcd_config_gpio(void)
{
//    Pad_Config( LCD_SPI_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE,  PAD_OUT_ENABLE, PAD_OUT_HIGH );
    Pad_Config( LCD_SPI_CLK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH );
    Pad_Config( LCD_SPI_MOSI_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH ); 
    
//    Pinmux_Config( LCD_SPI_CS_PIN, LCD_SPI_CS_PIN_MUX ); 
    Pinmux_Config( LCD_SPI_CLK_PIN, LCD_SPI_CLK_PIN_MUX ); 
    Pinmux_Config( LCD_SPI_MOSI_PIN, LCD_SPI_MOSI_PIN_MUX ); 
    
//    Pad_Config( LCD_DISP_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_HIGH );
//    Pinmux_Config( LCD_DISP_PIN, DWGPIO ); 
//    Pad_Config( LCD_EXTCOM_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_HIGH );
//    Pinmux_Config( LCD_EXTCOM_PIN, DWGPIO ); 
    LCD_CS_LOW;
    
    RCC_PeriphClockCmd( APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE); 
    RCC_PeriphClockCmd( LCD_SPI_Periph, LCD_SPI_PeriphClk, ENABLE ); 
    
    // SPI Init 
    SPI_InitTypeDef spiInitStruct;
    SPI_StructInit( &spiInitStruct );
    spiInitStruct.SPI_Direction         = SPI_Direction_TxOnly;
    spiInitStruct.SPI_Mode              = SPI_Mode_Master;
    spiInitStruct.SPI_DataSize          = SPI_DataSize_8b;
    spiInitStruct.SPI_CPOL              = SPI_CPOL_Low;
    spiInitStruct.SPI_CPHA              = SPI_CPHA_1Edge;
    spiInitStruct.SPI_BaudRatePrescaler = 40; 
    spiInitStruct.SPI_FrameFormat       = SPI_Frame_Motorola;
    spiInitStruct.SPI_SwapTxBitEn       = 1;
    SPI_Init( LCD_SPI_BUS, &spiInitStruct ); 
    SPI_Cmd( LCD_SPI_BUS, ENABLE );     
    
    // DMA Init
    NVIC_InitTypeDef nvicInitStruct;
    nvicInitStruct.NVIC_IRQChannel = LCD_DMA_CHANNEL_IRQ;
    nvicInitStruct.NVIC_IRQChannelPriority = 3; 
    nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &nvicInitStruct ); 
    
    /* Initialize GDMA peripheral */
    GDMA_InitTypeDef gdmaInitStruct;
    GDMA_StructInit( &gdmaInitStruct ); 
    gdmaInitStruct.GDMA_ChannelNum                  = LCD_DMA_CHANNEL_NUM;
    gdmaInitStruct.GDMA_DIR                         = GDMA_DIR_MemoryToPeripheral;
    gdmaInitStruct.GDMA_BufferSize                  = 240*240*2;
    gdmaInitStruct.GDMA_SourceInc                   = DMA_SourceInc_Inc;
    gdmaInitStruct.GDMA_DestinationInc              = DMA_DestinationInc_Fix;
    gdmaInitStruct.GDMA_SourceDataSize              = GDMA_DataSize_Byte;
    gdmaInitStruct.GDMA_DestinationDataSize         = GDMA_DataSize_Byte;
    gdmaInitStruct.GDMA_SourceMsize                 = GDMA_Msize_1;
    gdmaInitStruct.GDMA_DestinationMsize            = GDMA_Msize_1;
    gdmaInitStruct.GDMA_SourceAddr                  = 0;
    gdmaInitStruct.GDMA_DestinationAddr             = (uint32_t)(LCD_SPI_BUS->DR);
    gdmaInitStruct.GDMA_DestHandshake               = GDMA_Handshake_SPI0_TX;//GDMA_Handshake_SPI1_TX
    
    GDMA_Init( LCD_DMA_CHANNEL_INDEX, &gdmaInitStruct );
    GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE ); 
    
    // IO Init
    // GPIO_InitTypeDef gpioInitStruct;
    // GPIO_StructInit( &gpioInitStruct ); 
    // gpioInitStruct.GPIO_Pin = GPIO_GetPin( LCD_RS_PIN ) ; 
    // gpioInitStruct.GPIO_Mode = GPIO_Mode_OUT;
    // gpioInitStruct.GPIO_ITCmd = DISABLE;
    // GPIO_Init( &gpioInitStruct );
    
//    SPI_SetCSNumber(LCD_SPI_BUS, 0);
}

void lcd_gdma_init(void)
{
    RCC_PeriphClockCmd( APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE); 
    GDMA_Cmd( LCD_DMA_CHANNEL_NUM, DISABLE ); 
    
    NVIC_InitTypeDef nvicInitStruct;
    nvicInitStruct.NVIC_IRQChannel = LCD_DMA_CHANNEL_IRQ;
    nvicInitStruct.NVIC_IRQChannelPriority = 3; 
    nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &nvicInitStruct ); 
    
    /* Initialize GDMA peripheral */
    GDMA_InitTypeDef gdmaInitStruct;
    GDMA_StructInit( &gdmaInitStruct ); 
    gdmaInitStruct.GDMA_ChannelNum                  = LCD_DMA_CHANNEL_NUM;
    gdmaInitStruct.GDMA_DIR                         = GDMA_DIR_MemoryToPeripheral;
    gdmaInitStruct.GDMA_BufferSize                  = 240*240*2;
    gdmaInitStruct.GDMA_SourceInc                   = DMA_SourceInc_Inc;
    gdmaInitStruct.GDMA_DestinationInc              = DMA_DestinationInc_Fix;
    gdmaInitStruct.GDMA_SourceDataSize              = GDMA_DataSize_Byte;
    gdmaInitStruct.GDMA_DestinationDataSize         = GDMA_DataSize_Byte;
    gdmaInitStruct.GDMA_SourceMsize                 = GDMA_Msize_1;
    gdmaInitStruct.GDMA_DestinationMsize            = GDMA_Msize_1;
    gdmaInitStruct.GDMA_SourceAddr                  = 0;
    gdmaInitStruct.GDMA_DestinationAddr             = (uint32_t)(LCD_SPI_BUS->DR);
    gdmaInitStruct.GDMA_DestHandshake               = GDMA_Handshake_SPI0_TX;
    
    GDMA_Init( LCD_DMA_CHANNEL_INDEX, &gdmaInitStruct );
    GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE );
    GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer ); 
    GDMA_INTConfig( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE ); 
}

void em_lcd_enter_dlps(void)
{
//    Pad_Config( LCD_SPI_CS_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    Pad_Config( LCD_SPI_CLK_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    Pad_Config( LCD_SPI_MOSI_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    
    // Pad_Config( LCD_RS_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_LOW ); 
    
}
void em_lcd_exit_dlps(void)
{
//    Pad_Config( LCD_SPI_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_LOW ); 
    Pad_Config( LCD_SPI_CLK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH ); 
    Pad_Config( LCD_SPI_MOSI_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH ); 
    
    // Pad_Config( LCD_RS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_LOW );    
}


DATA_RAM_FUNCTION
void lcd_dma_send(uint8_t* source_buf, uint32_t size)
{
//    m_lcd_write.state = fsmLCDUnderTransfering; 
    lcd_wrctl.isbusy = true;
    
    LCD_CS_HIGH;
    
    GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer );
    GDMA_SetBufferSize( LCD_DMA_CHANNEL_INDEX, size); 
    GDMA_SetSourceAddress( LCD_DMA_CHANNEL_INDEX, (uint32_t)source_buf );
    GDMA_Cmd( LCD_DMA_CHANNEL_NUM, ENABLE );
    SPI_GDMACmd( LCD_SPI_BUS, SPI_GDMAReq_Tx, ENABLE ); 
}

#pragma push
#pragma O0
DATA_RAM_FUNCTION
void em_lcd_wait_transfer_done(void)
{
    if( lcd_wrctl.isbusy )
    {
		uint32_t timeout = 50000;
        while( GDMA_GetTransferINTStatus( LCD_DMA_CHANNEL_NUM ) != SET && (timeout --) ); 
        GDMA_ClearINTPendingBit( LCD_DMA_CHANNEL_NUM, GDMA_INT_Transfer );
        lcd_wrctl.isbusy = false;
    }
}
#pragma pop 

void lcd_common_init(void)
{
    memset( (uint8_t*)&lcd_wrctl, 0, sizeof(lcd_wrctl) );
    lcd_config_gpio();
}

void em_lcd_init_dlps(void)
{
    lcd_dlps_index = xDLPS_register( &lcd_cbs );
    LOG1("[LCD]: DLPS index = %d\r\n", lcd_dlps_index );    
}
#endif // !MODULE_LCD_ENABLE
